#include "fox_mul.h"

//Multiplication sequentielle
void block_mult(int * A,int* B,int* C)
{
  int i,k;
  int N = taille_block*taille_block;
  for (i = 0 ; i < N ; i++){
    for(k = 0 ; k < taille_block ; k++){
      C[i] += A[i%taille_block+k]*B[i+k*taille_block];
    }
  }
}


//taille_mat : nbre d'element sur une ligne.
int * parseur(FILE*fd,int * taille_mat, int * nb_block)
{
  //On récupere le nombre de block.
  char bff[3]; //assuming nbr of blocks < 99
  fgets(bff,4,fd);
  int l = strlen(bff);
  bff[l-1] = '\0';
  *nb_block = atoi(bff);
  //  printf("nb_block : %d\n",nb_block);
  
  //Calcul de la taille de la matrice.
  //On utilise un appel systeme pour compter les lignes dans le fichier.
  int nb_ligne;
  FILE *f; 
  system("wc -l mat.data > tmp" ); 
  f = fopen("tmp","rw" ); 
  fscanf(f,"%d",&nb_ligne);
  
  *taille_mat = nb_ligne-1;
  
  int * matrice = malloc(sizeof(int)*(*taille_mat)*(*taille_mat));
  int k = 0;
  char buf[100];
  
  while(-1 != fscanf(fd,"%s",buf))
    {
      matrice[k] = atoi(buf);
      ++k;
    }
  //////////////////
  int j;
  for (j = 0; j<k ; ++j)
    {printf("%d ",matrice[j]);}
  printf("\n");
  //////////////// 
  return matrice;
}

int main(int argc, char* argv[])
{
  int tag;
  int myrank;
  int size;
  int nb_block;
  int taille_mat;
  int taille_block;
  int buf[2];
  MPI_Init(NULL,NULL);
  MPI_Status status;
  MPI_Comm_rank(MPI_COMM_WORLD,&myrank);
  MPI_Comm GridComm;
  MPI_Comm_size(MPI_COMM_WORLD,&size);

  if(myrank == 0)
    {
      char * matA = argv[1];
      char * matB = argv[2];
      //Chargement des matrices.
      int taille_matA;
      int nb_blockA;
      FILE * fdA = fopen(matA,"r");
      int* A = parseur(fdA,&taille_matA,&nb_blockA);
      /* printf("nb_block : %d\n",nb_block); */
      /* printf("taille_mat : %d\n",taille_mat); */
      int taille_matB;
      int nb_blockB;
      FILE * fdB = fopen(matB,"r");
      int* B = parseur(fdB,&taille_matB,&nb_blockB);
      
      //Verification de la cohérence des matrices
      if (taille_matB != taille_matA){
	printf("Les matrices sont de tailles différentes.\n");
	return 0;
      }
      if (nb_blockB != nb_blockA){
	printf("Les blocks sont de tailles différentes.\n");
	return 0;
      } 
      if(taille_matA%nb_blockA != 0)
	{
	  printf("Le nombre de blocks(%d) ne divise pas la taille de la matrice(%d)\n",nb_block,taille_mat);
	  return 0;    
	}
      if(size != nb_blockA){
	printf("Processus trop ou pas assez nombreux : nb_proc:%d nb_block:%d \n",size,nb_block);
	return 0;
      }
      nb_block = nb_blockA;
      taille_mat = taille_matA;
      buf[0] = nb_block;
      buf[1] = taille_mat;
    }
 
  //envoi de nb_block et taille_mat à tous les processus
  MPI_Bcast(buf, 2, MPI_INT, 0, MPI_COMM_WORLD);
  taille_block = taille_mat/nb_block;
  
  //Création de la grille de taille_block²
  int dims[2] = {nb_block,nb_block};
  int period[2] = {1,1};
  MPI_Cart_create(MPI_Comm,2,dims,period,1,&GridComm);
  int coord[2];
  int gridrank;
  MPI_Comm_rank(GridComm,&gridrank);
  MPI_Cart_coords(GridComm,gridrank,2,coord);
  
  //allocation des blocks locaux
  
  int * a = malloc(sizeof(int)*taille_block*taille_block);
  int * b = malloc(sizeof(int)*taille_block*taille_block);
  int * c = malloc(sizeof(int)*taille_block*taille_block);
  
  //Création du datatype nécéssaire
  MPI_Datatype Block;
  MPI_Type_vector(taille_block,taille_block,taille_mat,MPI_INT,&Block);
  MPI_Type_commit(&Block);
  int block_s = taille_block*taille_block;  
  
  //envoi des blocks spécifiques aux bons processus
  if(myrank == 0)
    {
      int i,j=0;
     
      for (i=1 ; i<nb_block*nb_block-1 ; ++i){
	  MPI_Send(A,1,Block,i,tag,GridComm);
	  MPI_Send(B,1,Block,i,tag,GridComm);
	}
      
      //Recopie du bloc[0,0] des matrices A et B dans a et b.
      while (j<taille_mat*taille_mat){
	for (i=O ; i<taille_block ; ++i){
	  a[i] = A[i+j];
	  b[i] = B[i+j]
	    }
	j+=taille_mat;
      }
    }
  //reception des blocs, et initialisation à 0 de c.
  
  if(myrank > 0)
    {
      MPI_Recv(a,block_s,MPI_INT,tag,GridComm,&status);
      MPI_Recv(b,block_s,MPI_INT,tag,GridComm,&status);
      for (i=0 ; i<block_s ; ++i)
	c[i] = 0;
    }
  //calcul
  
  for (i=0 ; i<block_s ; ++i)
    {
      printf("%d %d %d\n",a[i],b[i],c[i]);
    }
  //reduction de la matrice obtenue
  
  free(a);
  free(b);
  free(c);
  MPI_Finalize();
  return EXIT_SUCCESS;
}
